
using Midi::SequenceBuilder

const class Note : Rest
{
  static const Int defaultPitch := 0
  static const Int defaultVelocity := 100
  static const Str[] names := ["C","Cs","D","Ds","E","F","Fs","G","Gs","A","As","B"]

  static Str noteName(Int pitch) { names[pitch%12] }

  const Int pitch
  const Int velocity
  const Int[]? accord

  new make ( Int pitch:=defaultPitch,
            Num length:=1,
            Int velocity:=defaultVelocity) : super(length)
  {
    //echo ("***Note make $pitch $length $velocity ***")
    this.pitch = pitch
    this.velocity = velocity
  }

  internal new makeCopyOf (Note n, |This f|? f := null) : this.make(n.pitch,n.length,n.velocity) {
    this.accord = n.accord?.dup
    if (f != null) f(this)
  }

  override Note copy(|This f|? f := null) {
    Note.makeCopyOf(this,f)
  }

  @Operator
  Note get(Int octaveOffset) {
    copy{it.pitch=this.pitch+octaveOffset*12}
  }

  override Str name() {
    noteName(pitch) + (accord==null ? "" : "%"+ accord.map{noteName(it+pitch)}.join("%"))
  }

  Int octave() { pitch/12 }

  @Operator
  override This mult(Num n) {
    copy{it.length=this.length*n.toFloat}
  }

  @Operator
  override This div(Num n) {
    copy{it.length=this.length/n.toFloat}
  }

  Note s() {
    copy{it.pitch=this.pitch+1}
  }

  Note f() {
    copy{it.pitch=this.pitch-1}
  }

  Note addInterval(Int interv) {
    copy {
      it.accord = it.accord==null ? [interv] : it.accord.dup.add(interv)
    }
  }

  @Operator
  Note mod(Note n) {
    npitch := n.pitch
    if ((0..<12).contains(n.pitch)) {
      while (npitch<pitch) npitch += 12
      while (npitch>=pitch+12) npitch -= 12
    }
    return addInterval(npitch-pitch)
  }

  override Str toStr() {
    name +
      (octave==0 ? "" : "("+octave+")")+
      (length==1f? "" : "*"+length)
  }
  
  override Str dump() {
    "Note("+pitch+","+length+","+velocity+","+accord+")"
  }
}